/*Almighty Sapling's relay Planner
visual representation of CS and Skills.
Basic Familiar: Obtainable in a sign-free HC run with no particular skills or items.
*/

string projName="almighty-saplings-ash-trunk-planner";
//html-form properties
boolean spaced=false;
int newline=0;
string[int]tagList;

//data properties
record piece{
 string name;
 string link;
 string image;
 int r;
};
piece[string,string,string] allInfo;
piece[string,string,string] opts;
//  [type, cat, sort]
file_to_map("planner.txt",allInfo);
file_to_map("planner_"+my_name()+".txt",opts);
string[string] cscols;
string[string] fields=form_fields();

string prompt;

//consts
string wiki='http://kol.coldfront.net/thekolwiki/index.php/';
string famimage='http://images.kingdomofloathing.com/itemimages/';
string trophimage='http://images.kingdomofloathing.com/otherimages/trophy/';
string tatimage='http://images.kingdomofloathing.com/otherimages/sigils/';
string skillimage='http://images.kingdomofloathing.com/itemimages/';

//Add text to the file, adding proper padding for new lines.
void n(string text){
 if(!spaced){
  for i from 1 upto newLine write(" ");
  spaced=true;
 }
 write(text);
 return;
}
//Add text to file, including a hard return.
void nln(string text){
 n(text+"\n");
 spaced=false;
}
void nln(){
 n("\n");
 spaced=false;
}

void opentag(string tag,string options){
 n("<"+tag);
 if(options!="")n(" "+options);
 nln(">");
 tagList[count(tagList)+1]=tag;
 newLine+=1;
}
void opentag(string tag){
 opentag(tag,"");
}

string closetag(){
 newline-=1;
 if(spaced)nln();
 if(count(tagList)==0)return "html";
 string result=remove tagList[count(tagList)];
 nln("</"+result+">");
 return result;
}
string closetag(string totag){
 string r=closetag();
 while((r!="html")&&(r!=totag))r=closetag();
 return r;
}

void fileJS(){
 writeln("function clearCompare(e){e.elements[\"compare\"].value=\"\"; e.submit();}");
 writeln("function scrolled(){\n var queuediv=document.getElementById('queuediv');\n var hiddenY=window.pageYOffset;\n anchordiv=document.getElementById('queueanchor');\n var anchorTop=new elementXY(anchordiv);\n var anchorBottom=new Position(anchorTop.X+anchordiv.offsetWidth,anchorTop.Y+anchordiv.offsetHeight);\n  var nh=anchorTop.Y-hiddenY;\n  if (nh<0) nh=0;\n  if((nh+hiddenY+queuediv.offsetHeight)>anchorBottom.Y){nh=anchorBottom.Y-hiddenY-queuediv.offsetHeight;\n }\n queuediv.style.position='fixed';\n  queuediv.style.left=anchorTop.X+'px';\n  queuediv.style.top=nh+'px';\n }");
 writeln("///SOTC Drag And Drop Lists ///http://www.switchonthecode.com");
 writeln("function elementXY(element){var curleft=curtop=0; if(element==\"document\"){ this.X=0; this.Y=0; return this; } if(typeof(element) == \"string\") element = document.getElementById(element); if (element.offsetParent){ do{ curleft+=element.offsetLeft; curtop+=element.offsetTop;} while(element=element.offsetParent); } this.X=curleft; this.Y=curtop; }");
 writeln("function mouseRel(ev, element){var posx = 0; var posy = 0; if (!ev) var ev = window.event; if (ev.pageX || ev.pageY) { posx = ev.pageX; posy = ev.pageY; } else if (ev.clientX || ev.clientY) { posx = ev.clientX + document.body.scrollLeft; posy = ev.clientY + document.body.scrollTop; } exy=new elementXY(element); if (typeof(element) == 'string')element=document.getElementById(element); this.X=posx-exy.X-window.pageXOffset+element.scrollLeft; this.Y=posy-exy.Y-window.pageYOffset+element.scrollTop}");
 writeln("function Position(x, y){this.X = x; this.Y = y; this.Add = function(val){var newPos = new Position(this.X, this.Y); if(val != null){if(!isNaN(val.X)) newPos.X += val.X; if(!isNaN(val.Y)) newPos.Y += val.Y;}return newPos; }; this.Subtract = function(val){ var newPos = new Position(this.X, this.Y); if(val != null){if(!isNaN(val.X))newPos.X -= val.X; if(!isNaN(val.Y))newPos.Y -= val.Y;} return newPos; }; this.Min = function(val){ var newPos = new Position(this.X, this.Y); if(val == null)return newPos; if(!isNaN(val.X) && this.X > val.X) newPos.X = val.X; if(!isNaN(val.Y) && this.Y > val.Y)newPos.Y = val.Y; return newPos; }; this.Max = function(val){ var newPos = new Position(this.X, this.Y); if(val == null) return newPos; if(!isNaN(val.X) && this.X < val.X) newPos.X = val.X; if(!isNaN(val.Y) && this.Y < val.Y) newPos.Y = val.Y; return newPos;}; this.Bound = function(lower, upper){var newPos = this.Max(lower);return newPos.Min(upper);}; this.Check = function(){var newPos = new Position(this.X, this.Y); if(isNaN(newPos.X)) newPos.X = 0; if(isNaN(newPos.Y)) newPos.Y = 0; return newPos;}; this.Apply = function(element){ if(typeof(element) == \"string\") element = document.getElementById(element); if(element == null) return; if(!isNaN(this.X)) element.style.left = this.X + 'px'; if(!isNaN(this.Y)) element.style.top = this.Y + 'px';};}");
 writeln("function hookEvent(element, eventName, callback){if(typeof(element) == \"string\")element = document.getElementById(element); if(element == null) return; if(element.addEventListener){if(eventName == 'mousewheel')element.addEventListener('DOMMouseScroll', callback, false); element.addEventListener(eventName, callback, false);}else if(element.attachEvent)element.attachEvent(\"on\" + eventName, callback);}");
 writeln("function unhookEvent(element, eventName, callback){if(typeof(element) == \"string\")element = document.getElementById(element); if(element == null)return; if(element.removeEventListener){ if(eventName == 'mousewheel')element.removeEventListener('DOMMouseScroll', callback, false); element.removeEventListener(eventName, callback, false); }else if(element.detachEvent)element.detachEvent(\"on\" + eventName, callback);}");
 writeln("function cancelEvent(e){e = e ? e : window.event; if(e.stopPropagation)e.stopPropagation(); if(e.preventDefault)e.preventDefault(); e.cancelBubble = true; e.cancel = true; e.returnValue = false; return false;}");
 writeln("function getEventTarget(e){e = e ? e : window.event; return e.target ? e.target : e.srcElement;}");
 writeln("function absoluteCursorPosition(eventObj){eventObj = eventObj ? eventObj : window.event; if(isNaN(window.scrollX))return new Position(eventObj.clientX + document.documentElement.scrollLeft + document.body.scrollLeft,eventObj.clientY + document.documentElement.scrollTop + document.body.scrollTop); else return new Position(eventObj.clientX + window.scrollX, eventObj.clientY + window.scrollY);}");
 writeln("function dragObject(element, attachElement, lowerBound, upperBound, startCallback, moveCallback, endCallback, attachLater){if(typeof(element) == \"string\")element = document.getElementById(element); if(element == null)return; if(lowerBound != null && upperBound != null){var temp = lowerBound.Min(upperBound); upperBound = lowerBound.Max(upperBound); lowerBound = temp;}; var cursorStartPos = null; var elementStartPos = null; var dragging = false; var listening = false; var disposed = false; function dragStart(eventObj){if(dragging || !listening || disposed)return; dragging = true; if(startCallback != null)startCallback(eventObj, element); cursorStartPos = absoluteCursorPosition(eventObj); elementStartPos = new Position(parseInt(element.style.left), parseInt(element.style.top)); elementStartPos = elementStartPos.Check(); hookEvent(document, \"mousemove\", dragGo); hookEvent(document, \"mouseup\", dragStopHook); return cancelEvent(eventObj);}; function dragGo(eventObj){if(!dragging || disposed)return; var newPos = absoluteCursorPosition(eventObj); newPos = newPos.Add(elementStartPos).Subtract(cursorStartPos); newPos = newPos.Bound(lowerBound, upperBound); newPos.Apply(element); if(moveCallback != null)moveCallback(newPos, element, eventObj); return cancelEvent(eventObj);}; function dragStopHook(eventObj){dragStop(); return cancelEvent(eventObj);}; function dragStop(){if(!dragging || disposed)return; unhookEvent(document, \"mousemove\", dragGo); unhookEvent(document, \"mouseup\", dragStopHook); cursorStartPos = null; elementStartPos = null; if(endCallback != null)endCallback(element); dragging = false;}; this.Dispose = function(){if(disposed)return; this.StopListening(true); element = null; attachElement = null; lowerBound = null; upperBound = null; startCallback = null; moveCallback = null; endCallback = null; disposed = true;}; this.StartListening = function(){if(listening || disposed)return; listening = true; hookEvent(attachElement, \"mousedown\", dragStart);}; this.StopListening = function(stopCurrentDragging){if(!listening || disposed)return; unhookEvent(attachElement, \"mousedown\", dragStart); listening = false; if(stopCurrentDragging && dragging)dragStop();}; this.IsDragging = function(){return dragging;}; this.IsListening = function(){return listening;}; this.IsDisposed = function(){return disposed;}; if(attachElement == null)attachElement = element; this.StartListening();}");
 writeln("function dragCopy(element, attachElement, lowerBound, upperBound, startCallback, moveCallback, endCallback, attachLater){if(typeof(element) == \"string\")element = document.getElementById(element).cloneNode(true); if(element == null)return; if(lowerBound != null && upperBound != null){var temp = lowerBound.Min(upperBound); upperBound = lowerBound.Max(upperBound); lowerBound = temp;} var cursorStartPos = null; var elementStartPos = null; var dragging = false; var listening = false; var disposed = false; function dragStart(eventObj){if(dragging || !listening || disposed)return; dragging = true; if(startCallback != null)startCallback(eventObj, element); cursorStartPos = absoluteCursorPosition(eventObj); elementStartPos = new Position(parseInt(element.style.left), parseInt(element.style.top)); elementStartPos = elementStartPos.Check(); hookEvent(document, \"mousemove\", dragGo); hookEvent(document, \"mouseup\", dragStopHook); return cancelEvent(eventObj);}; function dragGo(eventObj){if(!dragging || disposed)return; var newPos = absoluteCursorPosition(eventObj); newPos = newPos.Add(elementStartPos).Subtract(cursorStartPos); newPos = newPos.Bound(lowerBound, upperBound); newPos.Apply(element); if(moveCallback != null)moveCallback(newPos, element, eventObj); return cancelEvent(eventObj);}; function dragStopHook(eventObj){dragStop(); return cancelEvent(eventObj);}; function dragStop(){if(!dragging || disposed)return; unhookEvent(document, \"mousemove\", dragGo); unhookEvent(document, \"mouseup\", dragStopHook); cursorStartPos = null; elementStartPos = null; if(endCallback != null)endCallback(element); dragging = false;}; this.Dispose = function(){if(disposed)return; this.StopListening(true); element = null; attachElement = null; lowerBound = null; upperBound = null; startCallback = null; moveCallback = null; endCallback = null; disposed = true;}; this.StartListening = function(){if(listening || disposed)return; listening = true; hookEvent(attachElement, \"mousedown\", dragStart);}; this.StopListening = function(stopCurrentDragging){if(!listening || disposed)return; unhookEvent(attachElement, \"mousedown\", dragStart); listening = false; if(stopCurrentDragging && dragging)dragStop();}; this.IsDragging = function(){return dragging;}; this.IsListening = function(){return listening;}; this.IsDisposed = function(){return disposed;}; if(attachElement == null)attachElement = element; this.StartListening();}");
 writeln("var List;var PlaceHolder;var HCQ;var SCQ;var QPH;var NewQ;");
 writeln("function load(){scrolled(); HCQ = document.getElementById(\"hcqueue\"); SCQ = document.getElementById(\"scqueue\"); QPH = document.createElement(\"div\"); QPH.className= \"queued\"; QPH.style.backgroundColor = \"rgb(0,0,255)\"; QPH.SourceI = null; NewQ=null; divs=document.getElementsByTagName(\"div\"); if(HCQ!=null)for(var i=0;i<divs.length;i++){if (divs[i].className==\"queued\") new dragObject(divs[i],null,null,null,qDragBegin,qDragMoved,qDragEnd,false);} divs=document.getElementsByTagName(\"img\"); if(HCQ!=null)for(var i=0;i<divs.length;i++){if (divs[i].className.indexOf(\"draggable\")>-1) new dragObject(divs[i],null,null,null,null,qTrackItem,qNewItem,false);} List = document.getElementById(\"list\"); PlaceHolder = document.createElement(\"div\"); PlaceHolder.className = \"list\"; PlaceHolder.style.backgroundColor = \"rgb(0,0,225)\"; PlaceHolder.SourceI = null; new dragObject(\"skli\", null, null, null, itemDragBegin, itemMoved, itemDragEnd, false); new dragObject(\"fali\", null, null, null, itemDragBegin, itemMoved, itemDragEnd, false); new dragObject(\"trli\", null, null, null, itemDragBegin, itemMoved, itemDragEnd, false); new dragObject(\"tali\", null, null, null, itemDragBegin, itemMoved, itemDragEnd, false);}");
 writeln("function itemDragBegin(eventObj, element)\n{element.style.top = element.offsetTop + 'px'; element.style.left = element.offsetLeft + 'px'; element.className = \"drag\"; PlaceHolder.style.height = element.style.height; List.insertBefore(PlaceHolder, element); PlaceHolder.SourceI = element;}");
 writeln("function itemMoved(newPos, element, eventObj){eventObj = eventObj ? eventObj : window.event; var yPos = newPos.Y + (eventObj.layerY ? eventObj.layerY : eventObj.offsetY); var temp; var bestItem = \"end\"; for(var i=0; i<List.childNodes.length; i++){if(List.childNodes[i].className == \"list\"){temp = parseInt(List.childNodes[i].style.height); if(temp >= yPos){bestItem = List.childNodes[i]; break;} yPos -= temp;}} if(bestItem == PlaceHolder)return; PlaceHolder.SourceI = bestItem; if(bestItem == PlaceHolder.SourceI){ if(bestItem != \"end\") List.insertBefore(PlaceHolder, List.childNodes[i+1]);else List.appendChild(PlaceHolder);} if(bestItem != \"end\")List.insertBefore(PlaceHolder, List.childNodes[i]);else List.appendChild(PlaceHolder);}");
 writeln("function itemDragEnd(element){if(PlaceHolder.SourceI != null){PlaceHolder.SourceI = null; List.replaceChild(element, PlaceHolder);} element.className = 'list'; element.style.top = '0px'; element.style.left = '0px'; var b=document.getElementById(\"hiddenbutton\"); var d=document.getElementById(\"oldorder\").innerHTML; var h=document.getElementById(\"order\"); var f=''; for(var i=0; i<List.childNodes.length; i++){if(List.childNodes[i].id == 'skli')f+='1'; if(List.childNodes[i].id == 'fali')f+='2'; if(List.childNodes[i].id == 'trli')f+='3'; if(List.childNodes[i].id == 'tali')f+='4';} h.value=f; if (f==d) b.style.display=\"none\";else b.style.display=\"inline\";}");
 writeln("function qDragBegin(eventObj, element){var cpos=new absoluteCursorPosition(eventObj); var mdif=new mouseRel(eventObj,element); element.style.top=element.offsetTop+'px'; element.style.left=element.offsetLeft+'px'; element.className = 'qdrag'; QPH.style.height = '20px'; element.parentNode.insertBefore(QPH, element); QPH.SourceI = element;}");
 writeln("function qDragMoved(newPos, element, eventObj){var bestItem='start'; var m=0; mouseAt=new mouseRel(eventObj,HCQ); mouseAt.Y+=HCQ.scrollTop; var past=false; if (mouseAt.Y>0){bestItem=HCQ.firstChild; m=0;} for(var i=0; i<HCQ.childNodes.length; i++){if((HCQ.childNodes[i].className!='queued')||(HCQ.childNodes[i]==element))continue; var mouseAt=new mouseRel(eventObj,HCQ.childNodes[i]); mouseAt.Y+=HCQ.scrollTop; if (mouseAt.Y>0){bestItem = HCQ.childNodes[i]; m=i;} if (mouseAt.Y>HCQ.childNodes[i].offsetHeight) bestItem='Hend';} mouseAt=new mouseRel(eventObj,SCQ); mouseAt.Y+=SCQ.scrollTop; if (mouseAt.Y>0){bestItem=SCQ.firstChild; m=0;} if (mouseAt.Y>0)for(var i=0; i<SCQ.childNodes.length; i++){if((SCQ.childNodes[i].className!='queued')||(SCQ.childNodes[i]==element))continue; var mouseAt=new mouseRel(eventObj,SCQ.childNodes[i]); mouseAt.Y+=SCQ.scrollTop; if (mouseAt.Y>0){bestItem = SCQ.childNodes[i]; m=i;} if (mouseAt.Y>SCQ.childNodes[i].offsetHeight) bestItem='Send';} var disposer=document.getElementById('qdispose'); mouseAt=new mouseRel(eventObj,disposer); PlaceHolder.SourceI=bestItem; if((mouseAt.Y>0)&&(mouseAt.X>0)&&(mouseAt.Y<disposer.offsetHeight)&&(mouseAt.X<disposer.offsetWidth)){bestItem='stop'; if(QPH.parentNode) QPH.parentNode.removeChild(QPH);} if(bestItem==QPH)return; if(bestItem==QPH.SourceI){ if(bestItem=='start') element.parentNode.insertBefore(QPH,element.ParentNode.firstChild); else if(bestItem!='stop')bestItem.parentNode.insertBefore(QPH, bestItem.parentNode.childNodes[m+1]); }; if(bestItem=='Hend'){HCQ.appendChild(QPH); return;} if(bestItem=='Send'){SCQ.appendChild(QPH); return;} QPH.SourceI = bestItem; if(bestItem=='start')element.parentNode.insertBefore(QPH,element.ParentNode.firstChild);else if(bestItem!='stop')bestItem.parentNode.insertBefore(QPH, bestItem.parentNode.childNodes[m]);}");
 writeln("function qDragEnd(element){if(QPH.SourceI != null){if(QPH.parentNode!=null)QPH.parentNode.replaceChild(element, QPH);} element.className = 'queued'; element.style.top = '0px'; element.style.left = '0px'; if (QPH.SourceI=='stop')element.parentNode.removeChild(element); QPH.SourceI=null; var scale=document.getElementById('hcitems'); var items=''; for(var i=0;i<HCQ.childNodes.length;i++) if(HCQ.childNodes[i].className=='queued')items+=HCQ.childNodes[i].id+','; scale.value=items; scale=document.getElementById('scitems'); items=''; for(var i=0;i<SCQ.childNodes.length;i++) if(SCQ.childNodes[i].className=='queued')items+=SCQ.childNodes[i].id+','; scale.value=items;}");
 writeln("function qTrackItem(newPos,element,eventObj){NewQ=null; var cpos=new absoluteCursorPosition(eventObj); var exy=new elementXY(HCQ); exy.X+=window.pageXOffset; exy.Y+=window.pageYOffset; cpos.X-=exy.X; cpos.Y-=exy.Y; if ((cpos.X>0)&&(cpos.X<HCQ.offsetWidth)&&(cpos.Y>0)&&(cpos.Y<HCQ.offsetHeight)) NewQ=HCQ; var cpos=new absoluteCursorPosition(eventObj); var exy=new elementXY(SCQ); exy.X+=window.pageXOffset; exy.Y+=window.pageYOffset; cpos.X-=exy.X; cpos.Y-=exy.Y; if ((cpos.X>0)&&(cpos.X<SCQ.offsetWidth)&&(cpos.Y>0)&&(cpos.Y<SCQ.offsetHeight)) NewQ=SCQ;}");
 writeln("function qNewItem(element){if (NewQ==null)return; var newdiv=document.createElement('div'); newdiv.className='queued'; newdiv.id=element.id; var wha=newdiv.id; re=/([^X]+)X(.+)/; info=re.exec(wha); switch (info[1]){case 'SC':case 'TT':case 'P':case 'S':case 'DB':case 'AT':wha=info[1]+': '; if (info[2]=='0')wha+='0 Buff';else if (info[2]=='0C')wha+='0 Combat';else wha+=info[2]; break; case 'SP':wha=info[2]+': Spooky'; break; case 'C09':wha=info[2]+\": Crimbo '09\"; break; case 'HB':wha='Hobopolis'; break; case 'BA':wha='Trader: '+info[2]; break; case 'UW':wha=info[2]+': Sea'; break; case 'C10':wha=\"Crimbo '10\"; break; case 'MR':wha='Bookshelf'; break; case 'GS':wha='Gnome'; break; case 'ST':wha='Slimetube'; break; case 'DR':wha='Dreadsylvania'; break; default:wha='Misc.'; break;} newdiv.innerHTML=element.parentNode.title+'&nbsp;|&nbsp;'+wha; NewQ.appendChild(newdiv); var scale=document.getElementById('hcitems'); var items=''; for(var i=0;i<HCQ.childNodes.length;i++) if(HCQ.childNodes[i].className=='queued')items+=HCQ.childNodes[i].id+','; scale.value=items; scale=document.getElementById('scitems'); items=''; for(var i=0;i<SCQ.childNodes.length;i++) if(SCQ.childNodes[i].className=='queued')items+=SCQ.childNodes[i].id+','; scale.value=items; dragObject(newdiv,null,null,null,qDragBegin,qDragMoved,qDragEnd,false); NewQ=null;}");
}

string getOpt(string optname){
 if(opts["INFO",""] contains optname)return opts["INFO","",optname].name;
 return "";
}
string getOpt(string optname,string def){
 string r=getOpt(optname);
 if(r=="")r=def;
 return r;
}

boolean newer(){
 int saved=getOpt("REV","0").to_int();
 int planned=svn_info(projName).revision;
 if(saved<planned)return true;
 return false;
}

void ascend(){
}

void purifyQueue(){
 int i=0;
 piece[string,string]q2;
 while(i<count(opts["QUEUE","HC"])){
  i+=1;
  if(opts["SKILL",opts["QUEUE","HC",i.to_string()].name,opts["QUEUE","HC",i.to_string()].link].r==2)continue;
  q2["HC",to_string(count(q2["HC"])+1)]=opts["QUEUE","HC",i.to_string()];
 }
 i=0;
 while(i<count(opts["QUEUE","SC"])){
  i+=1;
  if(opts["SKILL",opts["QUEUE","SC",i.to_string()].name,opts["QUEUE","SC",i.to_string()].link].r==2)continue;
  if(opts["SKILL",opts["QUEUE","SC",i.to_string()].name,opts["QUEUE","SC",i.to_string()].link].r==1)continue;
  q2["SC",to_string(count(q2["SC"])+1)]=opts["QUEUE","SC",i.to_string()];
 }
 opts["QUEUE"]=q2;
}

void handle_forms(){
 if((fields["sure"]=="yes")&&(fields["ascend"]=="Ascend"))ascend();
 if(newer())fields["reload"]="y";
 if(fields["saveq"]=="Save Queue"){
  remove opts["QUEUE"];
  int i=0;
  piece sk;
 print(fields["hcitems"]);
  matcher skillname=create_matcher("([^X\\W]*)X([^,]*)",fields["hcitems"]);
  while(find(skillname)){
   boolean flagged=false;
   for x from 1 upto i if((opts["QUEUE","HC",x.to_string()].name==skillname.group(1))&&(opts["QUEUE","HC",x.to_string()].link==skillname.group(2)))flagged=true;
   if(flagged)continue;
   i+=1;
   sk=opts["SKILL",skillname.group(1),skillname.group(2)];
   if(sk.r!=2){
    opts["QUEUE","HC",i.to_string()].name=skillname.group(1);
    opts["QUEUE","HC",i.to_string()].link=skillname.group(2);
   }else i-=1;
  }
 print(fields["scitems"]);
  skillname=create_matcher("([^X\\W]*)X([^,]*)",fields["scitems"]);
  i=0;
  while(find(skillname)){
   boolean flagged=false;
   foreach x,v in opts["QUEUE","HC"] if((v.name==skillname.group(1))&&(v.link==skillname.group(2)))flagged=true;
   for x from 1 upto i if((opts["QUEUE","SC",x.to_string()].name==skillname.group(1))&&(opts["QUEUE","SC",x.to_string()].link==skillname.group(2)))flagged=true;
   if(flagged)continue;
   i+=1;
   sk=opts["SKILL",skillname.group(1),skillname.group(2)];
   if((sk.r==0)||(sk.r==3)){
    opts["QUEUE","SC",i.to_string()].name=skillname.group(1);
    opts["QUEUE","SC",i.to_string()].link=skillname.group(2);
   }else i-=1;
  }
 }
 cscols["skillno"]="silver";
 cscols["skillyet"]="silver";
 cscols["skillsc"]="cyan";
 cscols["skillhc"]="lime";
 cscols["skillc"]="black";
 cscols["tatn"]="red";
 cscols["taty"]="green";
 cscols["tatc"]="black";
 cscols["troy"]="green";
 cscols["tron"]="red";
 cscols["troc"]="black";
 cscols["famn"]="red";
 cscols["famy"]="green";
 cscols["famt"]="blue";
 cscols["famp"]="cyan";
 cscols["famc"]="black";
 switch(fields["sub"]){
  case "Save":
   foreach i in cscols cscols[i]=getOpt(i,cscols[i]);
   foreach i in cscols if(fields contains i){
    cscols[i]=fields[i];
   }
   opts["INFO","","HIDDEN"].name=fields["hide"];
   break;
  case "Defaults":
   print("Restoring Defaults");
   fields["order"]="1234";
   opts["INFO","","HIDDEN"].name="";
   break;
 }
 if(!(fields contains "sub")){
  foreach i in cscols if(opts["INFO",""] contains i) cscols[i]=opts["INFO","",i].name;
 }
 foreach i in cscols{
  opts["INFO","",i].name=cscols[i];
 }
 if(fields["order"]!="")opts["INFO","","ORDER"].name=fields["order"];
 opts["INFO","","ORDER"].name=getOpt("ORDER","1234");
 map_to_file(opts,"planner_"+my_name()+".txt");
}

void header(){
 newline=0;
 writeln("<!doctype html>");
 opentag("html");
 opentag("head");
 opentag("style","type=\"text/css\"");
 writeln(".kolbut {border:2px black solid;background-color: #FFFFFF;color: #000000;margin:3px;}");
 writeln(".fam {width:30px; height:30px; border:0px;}");
 writeln(".tt {width:50px; height:50px; border:0px;}");
 writeln(".tabled {border-spacing:10px; margin-left:auto; margin-right:auto; text-align:center;}");
 writeln(".tabled td {padding:5px;}");
 writeln(".skillbig {margin-left:auto; margin-right:auto;}");
 writeln(".skilltable {border-spacing:0px; margin-left:auto; margin-right:auto; text-align:center;}");
 writeln(".skilltable td {padding:10px;}");
 writeln(".skilltable th a {color:black; text-decoration:none;}");
 writeln(".second {background-color:#F0F0F0;}");
 writeln(".ntitle td,th {padding:10px 10px 0px;}");
 writeln(".title td {padding:1px 0px 4px; border-width:1px 5px; border-color:white; border-spacing:1px;}");
 writeln(".skheader img {width:30px; height:30px; border-width:1px 6px; border-color:black}");
 write(".sknoperm img{width:30px; height:30px; border:5px solid ");
 writeln(getOpt("skillno")+";}");
 write(".skbought img {width:30px; height:30px; border:5px solid ");
 writeln(getOpt("skillyet")+";}");
 write(".skscperm img {width:30px; height:30px; border:5px solid ");
 writeln(getOpt("skillsc")+";}");
 write(".skhcperm img {width:30px; height:30px; border:5px solid ");
 writeln(getOpt("skillhc")+";}");
 write(".sknope img {width:30px; height:30px; border:5px solid ");
 writeln(getOpt("skillc")+";}");
 writeln(".qdrag {z-index:100; position:absolute;}");
 writeln(".queued {position:relative; z-index:1; top:0px; left:0px;}");
 writeln(".qdisp {position:relative; border:1px solid black; height:20px; width:80%; background-color:#30FFFF}");
 writeln(".queue {position:relative; border:1px solid black; height:150px; width:80%; overflow-y:scroll; overflow-x:hidden; background-color:#30FFFF}");
 writeln(".drag {z-index:100; position:absolute; opacity:.50; filter:alpha(opacity=50);}");
 writeln(".list {position:relative; z-index:1; opacity:1; filter:aplha(opacity=100); top:0px; left:0px;}");
 closetag();
 writeln("<script language=\"Javascript\" type=\"text/javascript\" src=\""+__FILE__+"?file=js\"></script>");
 closetag("head");
 opentag("body","onload=\"load();\" onscroll=\"scrolled();\"");
 return;
}

piece denorm(piece x,string s){
 if(x.name=="")x.name=s;
 if(x.link=="")x.link=x.name;
 if(x.image=="")x.image="nopic.gif";
 return x;
}

void getskills(){
 print("Collecting skill information");
 remove opts["SKILL"];
 string b=visit_url("charsheet.php");
 b=substring(b,b.index_of("height=1"));
 if(index_of(b,"Current Familiar")>0)b=substring(b,0,index_of(b,"Current Familiar"));
 string m=visit_url("campground.php?action=bookshelf");
 matcher am;
 piece this;
 foreach sort in allInfo["SKILL","MR"]{
  this=allInfo["SKILL","MR",sort];
  if((m.index_of(this.name)>0)||(m.index_of(this.link)>0))this.r=2;
  opts["SKILL","MR",sort]=this;
 }
 foreach sub,sort in allInfo["SKILL"]{
  if(sub=="MR")continue;
  this=allInfo["SKILL",sub,sort];
  if(this.name=="")this.name=sort;
  am=create_matcher(">"+this.name+"</a> \\((.)",b);
  if(find(am)){
   if(group(am,1)=="<")this.r=2;
   else this.r=1;
  }
  if((this.r==0)&&(index_of(b,">"+this.name+"</a>")>0))this.r=3;
  opts["SKILL",sub,sort]=this;
 }
}

void getfams(){
 print("Collecting familiar information.");
 remove opts["FAMILIAR"];
 string b=visit_url("familiarnames.php");
 string post=visit_url("ascensionhistory.php?back=other&who="+my_id());
 string pre=visit_url("ascensionhistory.php?who="+my_id()+"&prens13=1&back=other");
 matcher am;
 string n;
 piece this;
 foreach sub,sort in allInfo["FAMILIAR"]{
  this=denorm(allInfo["FAMILIAR",sub,sort],sort);
  if(index_of(b,this.name+"<")>0)this.r=1;
  am=create_matcher(this.name+" \\((\\d*)\\.?\\d*%\\)",post);
  while(find(am)&&(this.r<3)){
   if(group(am,1).to_int()>89)this.r=2;
   if(group(am,1).to_int()>99)this.r=3;
  }
  am=create_matcher(this.name+" \\((\\d*)\\.?\\d*%\\)",pre);
  while(find(am)&&(this.r<3)){
   if(group(am,1).to_int()>89)this.r=2;
   if(group(am,1).to_int()>99)this.r=3;
  }
  if(this.link==this.name)this.link="";
  if(this.name==sort)this.name="";
  opts["FAMILIAR",sub,sort]=this;
 }
}

void gettrophies(){
 print("Collecting trophy information.");
 remove opts["TROPHY"];
 string b=visit_url("trophies.php").to_lower_case();
 piece this;
 foreach sub,sort in allInfo["TROPHY"]{
  this=allInfo["TROPHY",sub,sort];
  if(index_of(b,this.name.to_lower_case())>0)this.r=1;
  opts["TROPHY",sub,sort]=this;
 }
}

void gettattoos(){
 print("Collecting tattoo information.");
 remove opts["TATTOO"];
 string b=visit_url("account_tattoos.php");
 piece this;
 foreach sub,sort in allInfo["TATTOO"]{
  this=allInfo["TATTOO",sub,sort];
  if(index_of(b,"/"+this.image)>0)this.r=1;
  opts["TATTOO",sub,sort]=this;
 }
 matcher m;
 m=create_matcher("/hobotat(\\d*)\\.gif",b);
 if(find(m)){
  opts["TATTOO","M","Hobo Tattoo"].r=group(m,1).to_int();
  opts["TATTOO","M","Hobo Tattoo"].image=group(m);
 }else{
  opts["TATTOO","M","Hobo Tattoo"].image="hobotat19.gif";
 }
 m=create_matcher("/sspd(?:.+?)(\\d+)(?:.+?)\\.gif",b);
 if(find(m)){
  opts["TATTOO","M","St. Sneaky Pete's Day"].r=group(m,1).to_int();
  opts["TATTOO","M","St. Sneaky Pete's Day"].image=group(m);
 }else{
  opts["TATTOO","M","St. Sneaky Pete's Day"].image="sspdfi5if.gif";
 }
 m=create_matcher("/aol(\\d*)\\.gif",b);
 if(find(m)){
  opts["TATTOO","M","A.W.O.L. Tattoo"].r=group(m,1).to_int();
  opts["TATTOO","M","A.W.O.L. Tattoo"].image=group(m);
 }else{
  opts["TATTOO","M","A.W.O.L. Tattoo"].image="aol5.gif";
 }
}

void contents(){
 int fams=0;
 int tours=0;
 int hunds=0;
 int tats=0;
 int trophs=0;
 int softc=0;
 int hardc=0;
 int trophiesOT=0;
 int tatsOT=0;
 int skillsOT=0;
 int famsOT=0;
 int trophiesTot=0;
 int tatsTot=0;
 int skillsTot=0;
 int famsTot=0;
 foreach sub, sort in opts["FAMILIAR"] switch(opts["FAMILIAR",sub,sort].r){
  case 3: hunds+=1;
  case 2: tours+=1;
  case 1: fams+=1;
  case 0: famsTot+=1;famsOT+=1;break;
  case -1: famsTot+=1;
 }
 foreach sub, sort in opts["SKILL"] switch(opts["SKILL",sub,sort].r){
  case 3: skillsTot+=1;skillsOT+=1;break;
  case 2: skillsTot+=1;skillsOT+=1;hardc+=1;break;
  case 1: skillsTot+=1;skillsOT+=1;softc+=1;break;
  case 0: skillsTot+=1;skillsOT+=1;break;
  case -1: skillsTot+=1;
 }
 foreach sub,sort in opts["TATTOO"] switch(opts["TATTOO",sub,sort].r){
  case -1: tatsTot+=1;break;
  case 0: tatsTot+=1;tatsOT+=1;break;
  default: tatsTot+=1;tatsOT+=1;tats+=1;
 }
 foreach sub,sort in opts["TROPHY"] switch(opts["TROPHY",sub,sort].r){
  case -1: trophiesTot+=1;break;
  case 0: trophiesTot+=1;trophiesOT+=1;break;
  default: trophiesTot+=1;trophiesOT+=1;trophs+=1;
 }
 nln("<br />Order:");
 opentag("form","method=\"POST\" action=\""+__FILE__+"\"");
 nln("<div id=\"oldorder\" style=\"display: none;\">"+getOpt("ORDER","1234")+"</div>");
 nln("<input type=\"hidden\" name=\"order\" id=\"order\" value=\""+getOpt("ORDER","1234")+"\">");
 opentag("div","id=\"list\" style=\"position:relative;\"");
 for i from 0 to 3 switch(char_at(getOpt("ORDER","1234"),i)){
  case "1":n("<div id=\"skli\" class=\"list\" style=\"height:20px\"><a href=\"#Skills\">Skills</a>: "+to_string(hardc)+":"+to_string(softc));
   nln(" /"+skillsOT+(skillsOT!=skillsTot?" (total:"+skillsTot+")":"")+"</div>");break;
  case "2":n("<div id=\"fali\" class=\"list\" style=\"height:20px\"><a href=\"#Familiars\">Familiars</a>: "+to_string(fams));
   nln(" /"+famsOT+(famsOT!=famsTot?" (total:"+famsTot+")":"")+"</div>");break;
  case "3":n("<div id=\"trli\" class=\"list\" style=\"height:20px\"><a href=\"#Trophies\">Trophies</a>: "+to_string(trophs));
   nln(" /"+trophiesOT+(trophiesOT!=trophiesTot?" (total:"+trophiesTot+")":"")+"</div>");break;
  case "4":n("<div id=\"tali\" class=\"list\" style=\"height:20px\"><a href=\"#Tattoos\">Tattoos</a>: "+to_string(tats));
   nln(" /"+tatsOT+(tatsOT!=tatsTot?" (total:"+tatsTot+")":"")+"</div>");break;
 }
 closetag();
 opentag("div","id=\"hiddenbutton\" style=\"display:none;\"");
 nln("<input type=\"submit\" name=\"sub\" value=\"Save\" />");
 closetag("form");
 if(prompt!=""){
  nln("<br /><h4>"+prompt+"</h4>");
 }
 nln("<br />CS: "+to_string(tats+trophs+fams)+"<br />");
 if(get_property("bankedKarma")!="")nln("<br />Karma: "+get_property("bankedKarma"));
 if(item_amount($item[instant karma])>0)nln("<br />Instant Karma: "+item_amount($item[instant karma])+" <a href=\""+__FILE__+"?action=diskarma\">[discard]</a>");
 nln("<br /><br />Tourguides: "+tours.to_string()+"<br />100% Runs: "+hunds.to_string());
}

string spaces(string s){
 matcher mat=create_matcher("\\s",s);
 return replace_all(mat,"_");
}

string queuername(string qit){
 matcher info=create_matcher("([\\d\\w]+)X:?([\\d\\w.]+)",qit);
 if(!info.find())return "";
 string wha=""; 
 switch(info.group(1)){
  case "SC":
  case "TT":
  case "P":
  case "S":
  case "DB":
  case "AT":
   wha=info.group(1)+": ";
   if(info.group(2)=="0")wha+="0 Buff";
   else if(info.group(2)=="0C")wha+="0 Combat";
   else wha+=info.group(2);
   break;
  case "SP":
   wha=info.group(2)+": Spooky";
   break;
  case "C09":
   wha=info.group(2)+": Crimbo '09";
   break;
  case "HB":
   wha="Hobopolis";
   break;
  case "BA":
   wha="Trader: "+info.group(2);
   break;
  case "UW":
   wha=info.group(2)+": Sea";
   break;
  case "C10":
   wha="Crimbo '10";
   break;
  case "MR":
   wha="Bookshelf";
   break;
  case "GS":
   wha="Gnome";
   break;
  case "ST":
   wha="Slimetube";
   break;
  case "DR":
   wha="Dreadsylvania";
   break;
  default:
   wha="Misc.";
   break;
 }
 return "&nbsp;|&nbsp;"+wha;
}

string linkify(string link){
 matcher m=create_matcher("\"",link);
 return m.replace_all("&quot;");
}

void skills(boolean comp){
 piece sk;
 if(!comp){
  nln("<a name=\"Skills\"></a><h4>Skills</h4>");
  opentag("form","action=\""+__FILE__+"\" method=\"POST\" id=\"compform\"");
   nln("Compare to: <input type=\"text\" name=\"compare\" id=\"compare\" />");
   if(fields contains "compare")nln("<input class=\"kolbut\" type=\"button\" name=\"compare2\" id=\"compare2\" value=\"Clear\" onClick=\"clearCompare(this.form);\"/>");
  closetag();
  opentag("table","class=\"skillbig\"");
  opentag("tr");
  opentag("td");
 }else{
  opentag("td");
 }
 opentag("table","class=\"skilltable"+(comp?" second":"")+"\"");
 if(fields contains "compare")nln("<tr><td colspan=\""+(comp?"6\">"+fields["compare"]:"7\">"+my_name())+"</td></tr>");
 opentag("tr");
 if(!comp)nln("<th></th>");
 for i from 11 to 16{ //HEADER ICONS
  sk=denorm(opts["TATTOO","C",to_string(i)],to_string(i));
  nln("<th><a href=\""+wiki+sk.link+"\" title=\""+sk.name+"\" target=\"_blank\" class=\"skheader\"><img src=\""+tatimage+spaces(sk.image)+"\"></a></th>");
 }
 closetag();
 string sort;
 string th;
 for g from 0 to 34{ //0-31 Guild, 32 Spooky, 33 Sea, 34 Crimbo 09
  opentag("tr");
  switch(g){
   case 32:
    sort="SP";
    th="<a href=\""+wiki+"Spookyraven Manor Quest\">Spooky</a>";
    break;
   case 33:
    sort="UW";
    th="<a href=\""+wiki+"The Sea\">Sea</a>";
    break;
   case 34:
    sort="C09";
    th="<a href=\""+wiki+"The Crimbo Cartel\">Crimbo '09</a>";
    break;
   default:
    sort=to_string(g/2);
    if(g%2==1)sort=sort+'B';
    th='Level '+sort;
    break;
  }
  if(!comp){
   if((g<32)&&(g%2==0))nln("<th rowspan='2'>"+th+"</th>");
   if(g>31)nln("<th>"+th+"</th>");
  }
  foreach sub in $strings[SC,TT,P,S,DB,AT]{
   if(g<32)sk=denorm(opts["SKILL",sub,sort],sort);
   else sk=denorm(opts["SKILL",sort,sub],sub);
   if(sk.name==sort){
    nln("<td></td>");
    continue;
   }
   n("<td");
   switch(g){
    case 32:n(" style=\"background:#DEDBDB\">");break;
    case 33:n(" style=\"background:#50B8FF\">");break;
    case 34:n(" style=\"background:#D9D919\">");break;
    default:n(">");break;
   }
   n("<a href=\""+wiki+sk.link+"\" title=\""+sk.name+"\" target=\"_blank\" class=\"");
   switch(sk.r){
    case 3:n("skbought");break;
    case 2:n("skhcperm");break;
    case 1:n("skscperm");break;
    case 0:n("sknoperm");break;
    case -1:n("sknope");break;
   }
   n("\"><img src=\""+skillimage+sk.image+"\" class=\"draggable\" id=\"");
   if(g<32)nln(sub+"X"+sort+"\"></a></td>");
   else nln(sort+"X"+sub+"\"></a></td>");
  }
  closetag();
 }//SC-Cold TT-Spooky P-Stench S-Hot DB-Sleaze Elf-AT
 for i from 1 to 2{//HOBOPOLIS SPELLBOOKS
  opentag("tr");
  if((i==1)&&(!comp))nln("<th rowspan=\"2\"><a href=\""+wiki+"Hobopolis\">Hobopolis</a></th>");
  foreach sub in $strings[COLD,SPOOKY,STENCH,HOT,SLEAZE,ELF]{
   sk=denorm(opts["SKILL","HB",sub+i.to_string()],sub+i.to_string());
   n("<td style=\"background:");
   switch(sub){
    case "COLD":n("#B8D3FE");break;
    case "SPOOKY":n("#DEDBDB");break;
    case "STENCH":n("#A8D8B8");break;
    case "HOT":n("#FFA0A0");break;
    case "SLEAZE":n("#FAC1F5");break;
    case "ELF":n("#D9D919");break;
   }
   n("\"><a href=\""+wiki+sk.link+"\" title=\""+sk.name+"\" target=\"_blank\" class=\"");
   switch(sk.r){
    case 3:n("skbought");break;
    case 2:n("skhcperm");break;
    case 1:n("skscperm");break;
    case 0:n("sknoperm");break;
    case -1:n("sknope");break;
   }
   nln("\"><img src=\""+skillimage+sk.image+"\" class=\"draggable\" id=\"HBX"+sub+i.to_string()+"\"></a></td>");
  }
  closetag();
 }
 opentag("tr");
 if(!comp)nln("<th><a href=\""+wiki+"The Traveling Trader\">Traveling Trader</a></th>");
 foreach i in $strings[SC,TT,P,S,DB,AT]{//TRAVELING TRADER SKILLS
  sk=denorm(opts["SKILL","BA",i],i);
  n("<td style=\"background:#F570F5\"><a href=\""+wiki+sk.link+"\" title=\""+sk.name+"\" target=\"_blank\" class=\"");
  switch(sk.r){
   case 3:n("skbought");break;
   case 2:n("skhcperm");break;
   case 1:n("skscperm");break;
   case 0:n("sknoperm");break;
   case -1:n("sknope");break;
  }
  nln("\"><img src=\""+skillimage+sk.image+"\" class=\"draggable\" id=\"BAX"+i+"\"></a></td>");  
 }
 closetag();
 opentag("tr");
 if(!comp)nln("<th>Hodgman's Journals</th>");//HODGMAN SKILLS
 for i from 1 to 4{
  sk=denorm(opts["SKILL","HB","H"+i.to_string()],"H"+i.to_string());
  n("<td style=\"background:#FFA0E0\"><a href=\""+wiki+sk.link+"\" title=\""+sk.name+"\" target=\"blank\" class=\"");
  switch(sk.r){
   case 3:n("skbought");break;
   case 2:n("skhcperm");break;
   case 1:n("skscperm");break;
   case 0:n("sknoperm");break;
   case -1:n("sknope");break;
  }
  nln("\"><img src=\""+skillimage+sk.image+"\" class=\"draggable\" id=\"HBXH"+i.to_string()+"\"></a></td>");
 }
 sk=denorm(opts["SKILL","BA","RG"],"RG");//RAINBOW GRAVITATION
 n("<td style=\"background:#F570F5\"><a href=\""+wiki+sk.link+"\" title=\""+sk.name+"\" target=\"blank\" class=\"");
 switch(sk.r){
  case 3:n("skbought");break;
  case 2:n("skhcperm");break;
  case 1:n("skscperm");break;
  case 0:n("sknoperm");break;
  case -1:n("sknope");break;
 }
 nln("\"><img src=\""+skillimage+sk.image+"\" class=\"draggable\" id=\"BAXRG\"></a></td>"); 
 sk=denorm(opts["SKILL","HB","AT1"],"AT1");//first AT HOBOP SKILL
 n("<td style=\"background:#FFA0E0\"><a href=\""+wiki+sk.link+"\" title=\""+sk.name+"\" target=\"blank\" class=\"");
 switch(sk.r){
  case 3:n("skbought");break;
  case 2:n("skhcperm");break;
  case 1:n("skscperm");break;
  case 0:n("sknoperm");break;
  case -1:n("sknope");break;
 }
 nln("\"><img src=\""+skillimage+sk.image+"\" class=\"draggable\" id=\"HBXAT1\"></a></td>"); 
 closetag();
 opentag("tr");
 if(!comp)nln("<th><a href=\"CRIMBCO Headquarters\">Crimbo '10</a></th>");
 for i from 1 to 5{//CRIMBO 10 SKILLS
  sk=denorm(opts["SKILL","C10",i.to_string()],i.to_string());
  n("<td style=\"background:#D9D919\"><a href=\""+wiki+sk.link+"\" title=\""+sk.name+"\" target=\"blank\" class=\"");
  switch(sk.r){
   case 3:n("skbought");break;
   case 2:n("skhcperm");break;
   case 1:n("skscperm");break;
   case 0:n("sknoperm");break;
   case -1:n("sknope");break;
  }
  nln("\"><img src=\""+skillimage+sk.image+"\" class=\"draggable\" id=\"C10X"+i.to_string()+"\"></a></td>"); 
 }
 sk=denorm(opts["SKILL","HB","AT2"],"AT2");//second AT HOBOP SKILL
 n("<td style=\"background:#FFA0E0\"><a href=\""+wiki+sk.link+"\" title=\""+sk.name+"\" target=\"blank\" class=\"");
 switch(sk.r){
  case 3:n("skbought");break;
  case 2:n("skhcperm");break;
  case 1:n("skscperm");break;
  case 0:n("sknoperm");break;
  case -1:n("sknope");break;
 }
 nln("\"><img src=\""+skillimage+sk.image+"\" class=\"draggable\" id=\"HBXAT2\"></a></td>");
 closetag();
 opentag("tr");
 if(!comp)nln("<th rowspan=\"3\"><a href=\""+wiki+"The Machine\">Dreadsylvania Skills</a></th>");
 for i from 1 to 3{
  if(i>1)opentag("tr"); 
  for i2 from 1 to 5{
   sk=denorm(opts["SKILL","DR",to_string(i*10+i2)],to_string(i*10+i2));
   n("<td><a href=\""+wiki+sk.link+"\" title=\""+sk.name+"\" target=\"blank\" class=\"");
   switch(sk.r){
    case 3:n("skbought");break;
    case 2:n("skhcperm");break;
    case 1:n("skscperm");break;
    case 0:n("sknoperm");break;
    case -1:n("sknope");break;
   }
   nln("\"><img src=\""+skillimage+sk.image+"\" class=\"draggable\" id=\"DRX"+to_string(i*10+i2)+"\"></a></td>");
  }
  sk=denorm(opts["SKILL","HB","AT"+to_string(i+2)],"AT"+to_string(i+2));//AT HOBOP SKILLS
  n("<td style=\"background:#FFA0E0\"><a href=\""+wiki+sk.link+"\" title=\""+sk.name+"\" target=\"blank\" class=\""); 
  switch(sk.r){
   case 3:n("skbought");break;
   case 2:n("skhcperm");break;
   case 1:n("skscperm");break;
   case 0:n("sknoperm");break;
   case -1:n("sknope");break;
  }
  nln("\"><img src=\""+skillimage+sk.image+"\" class=\"draggable\" id=\"HBXAT"+to_string(i+2)+"\"></a></td>");   
  closetag();
 }
 opentag("tr");
 if(!comp)nln("<th><a href=\""+wiki+"The Slime Tube\">Slime Soaked Skills</a></th>");
 for i from 1 to 3{//SLIME SKILLS
  sk=denorm(opts["SKILL","ST",i.to_string()],i.to_string());
  n("<td style=\"background:#A0FFA0\"><a href=\""+wiki+sk.link+"\" title=\""+sk.name+"\" target=\"blank\" class=\"");
  switch(sk.r){
   case 3:n("skbought");break;
   case 2:n("skhcperm");break;
   case 1:n("skscperm");break;
   case 0:n("sknoperm");break;
   case -1:n("sknope");break;
  }
  nln("\"><img src=\""+skillimage+sk.image+"\" class=\"draggable\" id=\"STX"+i.to_string()+"\"></a></td>"); 
 }
 n("<td></td><td></td><td></td>");
 closetag();
 opentag("tr");
 if(!comp)nln("<th><a href=\""+wiki+"Fragnk, the Regnaissance Gnome\">Gnome Skills</a></th>");
 for i from 1 to 5{//GNOME SKILLS
  sk=denorm(opts["SKILL","GS",i.to_string()],i.to_string());
  n("<td><a href=\""+wiki+sk.link+"\" title=\""+sk.name+"\" target=\"blank\" class=\"");
  switch(sk.r){
   case 3:n("skbought");break;
   case 2:n("skhcperm");break;
   case 1:n("skscperm");break;
   case 0:n("sknoperm");break;
   case -1:n("sknope");break;
  }
  nln("\"><img src=\""+skillimage+sk.image+"\" class=\"draggable\" id=\"GSX"+i.to_string()+"\"></a></td>"); 
 }
 closetag();
 int rows=0;
 string[string]books;
 foreach book in opts["SKILL","MR"]{
  if(book.substring(1).to_int()%6==1)rows+=1;
  sk=denorm(opts["SKILL","MR",book],book);
  if((book.substring(1).to_int()%6==1)&&(book.substring(1).to_int()>1))books[book.substring(0,1)]+="</tr><tr style=\"background:#FFF3C3\">";
  books[book.substring(0,1)]+="<td><a href=\""+wiki+sk.link+"\" title=\""+sk.name.linkify()+"\" target=\"blank\" class=\"";
  switch(sk.r){
   case 3:books[book.substring(0,1)]+="skbought";break;
   case 2:books[book.substring(0,1)]+="skhcperm";break;
   case 1:books[book.substring(0,1)]+="skscperm";break;
   case 0:books[book.substring(0,1)]+="sknoperm";break;
   case -1:books[book.substring(0,1)]+="sknope";break;
  }
  books[book.substring(0,1)]+="\"><img src=\""+skillimage+sk.image+"\" class=\"draggable\" id=\"";
  books[book.substring(0,1)]+="MRX"+book+"\"></a></td>";
  books[book.substring(0,1)+"C"]=to_string((books[book.substring(0,1)+"C"].to_int()+5)%6);
 }
 opentag("tr","style=\"background:#FFF3C3\"");
 if(!comp)nln("<th rowspan=\""+rows+"\"><a href=\""+wiki+"Mystical Bookshelf\">Mystical Bookshelf</a></th>");
 n(books["T"]);
 for i from 1 upto books["TC"].to_int() n("<td></td>");
 closetag();
 opentag("tr","style=\"background:#FFF3C3\"");
 nln(books["L"]);
 for i from 1 upto books["LC"].to_int() n("<td></td>");
 closetag();
 opentag("tr","style=\"background:#FFF3C3\"");
 nln(books["G"]);
 for i from 1 upto books["GC"].to_int() n("<td></td>");
 closetag();
 int hbat=0;
 boolean firstrow=true;
 record titles{
  int cols;
  string title;
 };
 titles[int]titleLine;
 string subtitle="";
 matcher st=create_matcher(":(.+?)\\.","");
 rows=ceil(count(opts["SKILL","Etc"])/6.0)*2;
 foreach sub in opts["SKILL","Etc"]{//unclassed skills
  if(hbat==0)opentag("tr","class=\"ntitle\"");
  if((firstrow)&&(!comp)){
   firstrow=false;
   nln("<th rowspan=\""+rows.to_string()+"\">Misc. Skills</th>");
  }
  hbat+=1;
  sk=denorm(opts["SKILL","Etc",sub],sub);
  st.reset(sub);
  subtitle="";
  if(st.find())subtitle=st.group(1);
  if((count(titleline)==0)||(titleLine[count(titleline)].title!=subtitle)){
   titleLine[count(titleLine)+1].title=subtitle;
   titleLine[count(titleLine)].cols=1;
  }else titleLine[count(titleLine)].cols+=1;
  n("<td><a href=\""+wiki+sk.link+"\" title=\""+sk.name+"\" target=\"blank\" class=\"");
  switch(sk.r){
   case 3:n("skbought");break;
   case 2:n("skhcperm");break;
   case 1:n("skscperm");break;
   case 0:n("sknoperm");break;
   case -1:n("sknope");break;
  }
  nln("\"><img src=\""+skillimage+sk.image+"\" class=\"draggable\" id=\"EtcX"+sub+"\"></a></td>");   
  if(hbat==6){
   hbat=0;
   closetag();
   opentag("tr","class=\"title\"");
   foreach i,t in titleLine nln("<td"+(t.cols>1?" colspan=\""+t.cols+"\"":"")+(t.title==""?"":" style=\"background:#FFF3C3\"")+">"+t.title+"</td>");
   closetag();
   clear(titleLine);
  }
 }
 if(count(titleLine)>0){
  closetag();
  opentag("tr","class=\"title\"");
  foreach i,t in titleLine nln("<td"+(t.cols>1?" colspan=\""+t.cols+"\"":"")+(t.title==""?"":" style=\"background:#FFF3C3\"")+">"+t.title+"</td>");
 }
 closetag("table");
 closetag();
 if((getOpt("HIDDEN")=="yes")||(fields contains "compare")){
 }
 else{
  opentag("td","style=\"width:100%;\" id=\"queueanchor\"");
  opentag("div","id=\"queuediv\" style=\"width:55%\"");
  opentag("form","action=\""+__FILE__+"\" method=\"POST\"");
  nln("Queued for HC Perm");
  opentag("div","id=\"hcqueue\" class=\"queue\"");
  string qitems="";
  string qitem="";
  int qcount=0;
  piece res;
  foreach sub in opts["QUEUE","HC"] if(qcount<sub.to_int())qcount=sub.to_int();
  for sub from 1 upto qcount{
   res=opts["QUEUE","HC",sub.to_string()];
   qitem=res.name+"X"+res.link;
   opentag("div","class=\"queued\" id=\""+qitem+"\"");
   nln(opts["SKILL",res.name,res.link].name+queuername(qitem));
   qitems+=","+qitem;
   closetag();
  }
  if(qitems!="")qitems=substring(qitems,1);
  closetag("div");
  nln("<input type=\"hidden\" name=\"hcitems\" id=\"hcitems\" value=\""+qitems+"\" />");
  nln("Queued for SC Perm");
  opentag("div","id=\"scqueue\" class=\"queue\"");
  qitems="";
  qitem="";
  qcount=0;
  foreach sub in opts["QUEUE","SC"] if(qcount<sub.to_int())qcount=sub.to_int();
  for sub from 1 upto qcount{
   res=opts["QUEUE","SC",sub.to_string()];
   qitem=res.name+"X"+res.link;
   opentag("div","class=\"queued\" id=\""+qitem+"\"");
   nln(opts["SKILL",res.name,res.link].name+queuername(qitem));
   qitems+=","+qitem;
   closetag();
  }
  if(qitems!="")qitems=substring(qitems,1);
  closetag("div");
  nln("<input type=\"hidden\" name=\"scitems\" id=\"scitems\" value=\""+qitems+"\" />");
  nln("<input class=\"kolbut\" type=\"submit\" name=\"saveq\" value=\"Save Queue\" />&nbsp;<span class=\"qdisp\" id=\"qdispose\">Drag Here to Remove</span>");
 }
 if(!(fields contains "compare"))closetag("table");
}
void skills(){
 skills(false);
}

void compareSkills(){
 string w=fields["compare"];
 matcher m=create_matcher("[^\\d]",w);
 if(m.find()){
  w=visit_url("searchplayer.php?searching=Yep.&searchstring="+w+"&hardcoreonly=0");
  m=create_matcher('(?i)(\\d*)">'+fields["compare"]+'</a></b> (?: \\(PvP\\))?(?:<br>\\(<a target=mainpane href="showclan\\.php\\?whichclan=(\\d*))?',w);
  if(!find(m))w="0";
  else w=group(m,1);
 }
 w=visit_url("showplayer.php?who="+w);
 foreach a,b,x in opts["SKILL"]{
  m=create_matcher(x.name+"(?:</a>)? \\((.)",w);
  if(!m.find()){
   x.r=0;
   continue;
  }
  if(m.group(1)=="<")x.r=2;
  else x.r=1;
 }
 skills(true);
 closetag("table");
 file_to_map("planner_"+my_name()+".txt",opts);
}

void familiars(){
 piece fam;
 int i=0;
 nln("<a name=\"Familiars\"></a><h4>Familiars</h4>");
 nln("<br />Basic<br />");
 opentag("table","class=\"tabled\"");
  opentag("tr");
  foreach sort in opts["FAMILIAR","B"]{
   i+=1;
   if(i==13){
    closetag();
    opentag("tr");
    i=1;
   }
   fam=denorm(opts["FAMILIAR","B",sort],sort);
   n("<td style=\"background:");
   switch(fam.r){
    case 3:n(getOpt("famp"));break;
    case 2:n(getOpt("famt"));break;
    case 1:n(getOpt("famy"));break;
    case -1:n(getOpt("famc"));break;
    default:n(getOpt("famn"));break;
   }
   nln("\"><a href=\""+wiki+fam.link+"\" title=\""+fam.name+"\" target=\"_blank\"><img src=\""+famimage+fam.image+"\" class=\"fam\"></a></td>");
  }
 i=0;
 closetag("table");
 nln("<br /><br />Uncommon<br />");
 opentag("table","class=\"tabled\"");
  opentag("tr");
  foreach sort in opts["FAMILIAR","U"]{
   i+=1;
   if(i==13){
    closetag();
    opentag("tr");
    i=1;
   }
   fam=denorm(opts["FAMILIAR","U",sort],sort);
   n("<td style=\"background:");
   switch(fam.r){
    case 3:n(getOpt("famp"));break;
    case 2:n(getOpt("famt"));break;
    case 1:n(getOpt("famy"));break;
    case -1:n(getOpt("famc"));break;
    default:n(getOpt("famn"));break;
   }
   nln("\"><a href=\""+wiki+fam.link+"\" title=\""+fam.name+"\" target=\"_blank\"><img src=\""+famimage+fam.image+"\" class=\"fam\"></a></td>");
  }
 i=0;
 closetag("table");
 nln("<br /><br /><a href=\""+wiki+"Mr. Store\">Mr. A Familiars<a><br />");
 opentag("table","class=\"tabled\"");
 opentag("tr");
 nln("<td></td>");
 nln("<td>Jan</td>");
 nln("<td>Feb</td>");
 nln("<td>Mar</td>");
 nln("<td>Apr</td>");
 nln("<td>May</td>");
 nln("<td>Jun</td>");
 nln("<td>Jul</td>");
 nln("<td>Aug</td>");
 nln("<td>Sep</td>");
 nln("<td>Oct</td>");
 nln("<td>Nov</td>");
 nln("<td>Dec</td>");
 nln("<td>F/L</td>");
 nln("<td>V/L</td>");
 closetag();
 string ys;
 string ms;
 int lastyear=4;
 int lastmonth=0;
 int thisyear,thismonth;
 opentag("tr");
 nln("<td>2004</td>");
 foreach i in opts["FAMILIAR","M"]{
  thisyear=i.substring(0,2).to_int();
  thismonth=i.substring(2).to_int();
  if(thisyear>lastyear){
   closetag("tr");
   opentag("tr");
   nln("<td>20"+(thisyear<10?"0":"")+thisyear.to_string()+"</td>");
   lastmonth=0;
   lastyear=thisyear;
  }
  for gaps from lastmonth upto thismonth-2 nln("<td></td>");
  lastmonth=thismonth;
  fam=denorm(opts["FAMILIAR","M",i],i);
  n("<td style=\"background:");
  switch(fam.r){
   case 3:n(getOpt("famp"));break;
   case 2:n(getOpt("famt"));break;
   case 1:n(getOpt("famy"));break;
   case -1:n(getOpt("famc"));break;
   default:n(getOpt("famn"));break;
  }
  nln("\"><a href=\""+wiki+fam.link+"\" title=\""+fam.name+"\" target=\"_blank\"><img src=\""+famimage+fam.image+"\" class=\"fam\"></a></td>");
 }
 closetag("table");
}

void trophies(){
 piece tro;
 int tcount=1;
 foreach n in allInfo["TROPHY",""] if(n.to_int()>tcount)tcount=n.to_int();
 nln("<a name=\"Trophies\"></a><h4>Trophies</h4>");
 opentag("table","class=\"tabled\"");
 opentag("tr");
 for i from 1 to tcount {
  tro=denorm(opts["TROPHY","",i.to_string()],i.to_string());
  if((i%10==1)&&(i>1)){
   closetag();
   opentag("tr");
  }
  if(tro.name==i.to_string()){
   nln("<td></td>");
   continue;
  }
  n("<td style=\"background:");
  switch(tro.r){
   case 1:n(getOpt("troy"));break;
   case 0:n(getOpt("tron"));break;
   case -1:n(getOpt("troc"));break;
  }
  nln("\"><a href=\""+wiki+tro.link+"\" title=\""+tro.name+"\" target=\"_blank\"><img src=\""+trophimage+spaces(tro.image)+"\" class=\"tt\"></a></td>");
 }
 closetag("table");
}

void tattoos(){
 piece tat;
 int i=0;
 nln("<a name=\"Tattoos\"></a><h4>Tattoos</h4>");
 opentag("table","class=\"tabled\"");
 foreach j in opts["TATTOO","C"]{
  if(j.char_at(j.length()-1)=="1")opentag("tr");
  tat=denorm(opts["TATTOO","C",j],j);
  n("<td style=\"background:");
  switch(tat.r){
   case 1:n(getOpt("taty"));break;
   case 0:n(getOpt("tatn"));break;
   case -1:n(getOpt("tatc"));break;
  }
  nln("\"><a href=\""+wiki+tat.link+"\" title=\""+tat.name+"\" target=\"_blank\"><img src=\""+tatimage+spaces(tat.image)+"\" class=\"tt\"></a></td>");
  if(j.char_at(j.length()-1)=="6")closetag();
 }
 opentag("tr");
 nln("<td colspan=\"6\"></td>"); 
 foreach j in opts["TATTOO","A"]{
  if(j.to_int()%6==5){
   closetag();
   opentag("tr");
  }
  tat=denorm(opts["TATTOO","A",j],j);
  n("<td style=\"background:");
  switch(tat.r){
   case 1:n(getOpt("taty"));break;
   case 0:n(getOpt("tatn"));break;
   case -1:n(getOpt("tatc"));break;
  }
  nln("\"><a href=\""+wiki+tat.link+"\" title=\""+tat.name+"\" target=\"_blank\"><img src=\""+tatimage+spaces(tat.image)+"\" class=\"tt\"></a></td>");
 }
 closetag();
 opentag("tr");
 nln("<td colspan=\"6\"></td>");
 closetag();
 i=0;
 opentag("tr");
 foreach sort in opts["TATTOO","X"]{
  i+=1;
  if(i==7){
   closetag();
   opentag("tr");
   i=1;
  }
  tat=denorm(opts["TATTOO","X",sort],sort);
  n("<td style=\"background:");
  switch(tat.r){
   case 1:n(getOpt("taty"));break;
   case 0:n(getOpt("tatn"));break;
   case -1:n(getOpt("tatc"));break;
  }
  nln("\"><a href=\""+wiki+tat.link+"\" title=\""+tat.name+"\" target=\"_blank\"><img src=\""+tatimage+spaces(tat.image)+"\" class=\"tt\"></a></td>");
 } 
 closetag();
 opentag("tr");
 nln("<td colspan=\"6\"></td>");
 closetag();
 i=0;
 opentag("tr");
 foreach sort in opts["TATTOO","O"]{
  i+=1;
  if(i==7){
   closetag();
   opentag("tr");
   i=1;
  }
  tat=denorm(opts["TATTOO","O",sort],sort);
  n("<td style=\"background:");
  switch(tat.r){
   case 1:n(getOpt("taty"));break;
   case 0:n(getOpt("tatn"));break;
   case -1:n(getOpt("tatc"));break;
  }
  nln("\"><a href=\""+wiki+tat.link+"\" title=\""+tat.name+"\" target=\"_blank\"><img src=\""+tatimage+spaces(tat.image)+"\" class=\"tt\"></a></td>");
 }
 closetag();
 opentag("tr");
 nln("<td colspan=\"6\"></td>");
 closetag();
 i=0;
 opentag("tr");
 foreach sort in opts["TATTOO","M"]{
  i+=1;
  if(i==7){
   closetag();
   opentag("tr");
   i=1;
  }
  tat=denorm(opts["TATTOO","M",sort],sort);
  n("<td style=\"background:");
  switch(tat.r){
   case 0:n(getOpt("tatn"));break;
   case -1:n(getOpt("tatc"));break;
   default:n(getOpt("taty"));break;
  }
  nln("\"><a href=\""+wiki+tat.link+"\" title=\""+tat.name+"\" target=\"_blank\"><img src=\""+tatimage+spaces(tat.image)+"\" class=\"tt\"></a></td>");
 }
 closetag("table");
}

void information(){
 if (fields["reload"]=="y"){
  print("Refreshing data.","red");
  getskills();
  purifyQueue();
  getfams();
  gettrophies();
  gettattoos();
  opts["INFO","","REV"].name=svn_info(projName).revision;
  map_to_file(opts,"planner_"+my_name()+".txt");
  print("Data saved.","green");
 }
 contents();
 for i from 0 to 3 switch(char_at(getOpt("ORDER","1234"),i)){
  case "1":skills();
   if(fields contains "compare")compareSkills();
   break;
  case "2":familiars();break;
  case "3":trophies();break;
  case "4":tattoos();break;
 }
 return;
}

void options(){
 nln("<!--CUT FROM HERE-->");
 opentag("form","action=\""+__FILE__+"\" method=\"POST\"");
 nln("<br /><br />");
 opentag("center");
  opentag("table");
   nln("<tr><th colspan=2>Options</th></tr>");
   nln("<tr><td colspan=2><center>(see HTML <a href=\"http://www.w3schools.com/html/html_colornames.asp\">colors</a>)</center></td></tr>");
   opentag("tr");
    opentag("td");
     opentag("table");
     nln("<tr><th colspan=2>Skills</th></tr>");
     nln("<tr><td>Not Permed</td><td><input type=\"text\" name=\"skillno\" value=\""+getOpt("skillno")+"\" /></td></tr>");
     nln("<tr><td>Available</td><td><input type=\"text\" name=\"skillyet\" value=\""+getOpt("skillyet")+"\" /></td></tr>");
     nln("<tr><td>SC Permed</td><td><input type=\"text\" name=\"skillsc\" value=\""+getOpt("skillsc")+"\" /></td></tr>");
     nln("<tr><td>HC Permed</td><td><input type=\"text\" name=\"skillhc\" value=\""+getOpt("skillhc")+"\" /></td></tr>");
     nln("<tr><td>No Longer Available</td><td><input type=\"text\" name=\"skillc\" value=\""+getOpt("skillc")+"\" /></td></tr>");
     n("<tr><td>Hide Queue</td><td><input type=\"checkbox\" name=\"hide\" value=\"yes\"");
     if(getOpt("HIDDEN")=="yes")n(" checked=\"checked\"");
     nln("/></td></tr>");
    closetag("td");
    opentag("td");
     opentag("table");
     nln("<tr><th>Familiars</th></tr>");
     nln("<tr><td>Don't Have</td><td><input type=\"text\" name=\"famn\" value=\""+getOpt("famn")+"\" /></td></tr>");
     nln("<tr><td>Have</td><td><input type=\"text\" name=\"famy\" value=\""+getOpt("famy")+"\" /></td></tr>");
     nln("<tr><td>Toured</td><td><input type=\"text\" name=\"famt\" value=\""+getOpt("famt")+"\" /></td></tr>");
     nln("<tr><td>100% Toured</td><td><input type=\"text\" name=\"famp\" value=\""+getOpt("famp")+"\" /></td></tr>");
     nln("<tr><td>No Longer Available</td><td><input type=\"text\" name=\"famc\" value=\""+getOpt("famc")+"\" /></td></tr>");
   closetag("tr");
   opentag("tr");
    opentag("td");
     opentag("table");
     nln("<tr><th>Tattoos</th></tr>");
     nln("<tr><td>Don't Have</td><td><input type=\"text\" name=\"tatn\" value=\""+getOpt("tatn")+"\" /></td></tr>");
     nln("<tr><td>Have</td><td><input type=\"text\" name=\"taty\" value=\""+getOpt("taty")+"\" /></td></tr>");
     nln("<tr><td>No Longer Available</td><td><input type=\"text\" name=\"tatc\" value=\""+getOpt("tatc")+"\" /></td></tr>");
    closetag("td");
    opentag("td");
     opentag("table");
     nln("<tr><th>Trophies</th></tr>");
     nln("<tr><td>Don't Have</td><td><input type=\"text\" name=\"tron\" value=\""+getOpt("tron")+"\" /></td></tr>");
     nln("<tr><td>Have</td><td><input type=\"text\" name=\"troy\" value=\""+getOpt("troy")+"\" /></td></tr>");
     nln("<tr><td>No Longer Available</td><td><input type=\"text\" name=\"troc\" value=\""+getOpt("troc")+"\" /></td></tr>");
     closetag();
  closetag("table");
  nln("<input class=\"kolbut\" type=\"submit\" name=\"sub\" value=\"Save\" /><input class=\"kolbut\" type=\"submit\" name=\"sub\" value=\"Defaults\" />");
 closetag("form"); 
 opentag("form","method=\"POST\" action=\""+__FILE__+"\"");
  opentag("center");
  nln("<input type=\"hidden\" name=\"reload\" value=\"y\" />");
  nln("<input class=\"kolbut\" type=\"submit\" value=\"Reload Data\" />");
 closetag("center");
 nln("<!--TO HERE-->");
 closetag("form");
 return;
}

void main(){
 foreach k,v in fields if(v=="")remove fields[k];
 if(fields contains "action")switch(fields["action"]){
  case "diskarma":
   prompt+="You banked "+to_string(item_amount($item[Instant Karma])*11)+" karma.";
   while(item_amount($item[Instant Karma])>0)cli_execute("text inventory.php?which=1&action=discard&pwd&whichitem=4448");
   break;
 }
 if(fields contains "file")switch(fields["file"]){
  case "js":fileJS();
   return;
 }
 handle_forms();
 header();
 information();
 options();
 closetag("html");
}